package com.army.study.slice;

import com.army.study.ResourceTable;
import com.army.study.devices.SelectDeviceDialog;
import com.army.study.utils.CommonData;
import com.army.study.utils.LogUtil;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.ability.IAbilityConnection;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.text.Font;
import ohos.agp.utils.Color;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.window.dialog.CommonDialog;
import ohos.agp.window.dialog.IDialog;
import ohos.agp.window.dialog.ToastDialog;
import ohos.app.Context;
import ohos.bundle.AbilityInfo;
import ohos.bundle.ElementName;
import ohos.bundle.IBundleManager;
import ohos.data.distributed.common.KvManagerConfig;
import ohos.data.distributed.common.KvManagerFactory;
import ohos.distributedschedule.interwork.DeviceInfo;
import ohos.distributedschedule.interwork.DeviceManager;
import ohos.event.commonevent.*;
import ohos.rpc.*;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;

import static ohos.agp.components.ComponentContainer.LayoutConfig.MATCH_CONTENT;
import static ohos.security.SystemPermission.DISTRIBUTED_DATASYNC;

public class PlayAbilitySlice extends AbilitySlice {
    private static final String TAG = CommonData.TAG + PlayAbilitySlice.class.getSimpleName();
    private static final int PERMISSION_CODE = 20211018;
    // 本地端设备Id
    private String localDeviceId;
    // 是否为本地端
    private boolean isLocal = true;
    // 远程端设备Id
    private String remoteDeviceId;
    // Ability 连接接口
    private IAbilityConnection conn;
    // 标识是否已连接上
    private boolean isConnected = false;
    // 定义内部游戏远程代理
    private GameRemoteProxy proxy;
    // 定义保存设备信息列表
    private List<DeviceInfo> devices = new ArrayList<>();
    // 自定义公共事件
    private MyCommonEventSubscriber subscriber;
    // 发送事件时间
    private long sendTime = 0L;
    // 接收事件时间
    private long reciveTime = 0L;
    // 最大接收时间
    private static final int MAX_RECIVE_TIME = 500;
    // 是否共享
    private boolean isShare = true;
    // 延时时间
    private static final int DELAY_TIME = 10;

    // 记录当前要点击的数字
    private int flag = 1;
    // 记录开始时间
    private long startTime = -1;
    // 当前排列数
    private int arrayNumber = -1;
    // 当前列阵数
    private int currentCount = -1;
    // 记录当前点击后的按钮,方便点击下一个按钮时,恢复为默认按钮状态
    private Button beforeButton = null;
    // 奖状对话框
    private CommonDialog dialog = null;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_game_play_layout);
        // 动态授权
        grantPermission(getContext());
        // 头部初始化
        headerBar();
        // 初始化本地端视图
        initLocalView(intent);
        // 初始化远程端视图
        initRemoteView(intent);
        // 页脚初始化
        footerBar();
        // 订阅事件
        subscribe();
    }

    /**
     * 动态授权
     * @param context
     */
    private static void grantPermission(Context context) {
        LogUtil.info(TAG, "grantPermission");
        if (context.verifySelfPermission(DISTRIBUTED_DATASYNC) != IBundleManager.PERMISSION_GRANTED) {
            if (context.canRequestPermission(DISTRIBUTED_DATASYNC)) {
                context.requestPermissionsFromUser(new String[] {DISTRIBUTED_DATASYNC}, PERMISSION_CODE);
            }
        }
    }

    /**
     * 初始化标题按钮事件
     */
    private void headerBar() {
        Text title = (Text)findComponentById(ResourceTable.Id_title);
        title.setText("1024一起专注" + (isLocal ? "(本地端)" : "(远程端)"));

        // 回到游戏首页
        Image imgBack = (Image)findComponentById(ResourceTable.Id_imgBack);
        imgBack.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                terminateAbility();
            }
        });

        // 本地端才显示出分布式协同按钮
        Image imgTranfser = (Image) findComponentById(ResourceTable.Id_imgTransfer);
        imgTranfser.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                getDevices();
            }
        });
        if (isLocal) {
            imgTranfser.setVisibility(Component.VISIBLE);
        } else {
            imgTranfser.setVisibility(Component.INVISIBLE);
        }

    }

    /**
     * 获取设备信息
     */
    private void getDevices() {
        // 清空缓存
        if (devices.size() > 0) {
            devices.clear();
        }
        // 获取在线设备信息
        List<DeviceInfo> deviceInfos =
                DeviceManager.getDeviceList(ohos.distributedschedule.interwork.DeviceInfo.FLAG_GET_ONLINE_DEVICE);
        LogUtil.info(TAG, "deviceInfos size is :" + deviceInfos.size());
        // 添加到设备列表
        devices.addAll(deviceInfos);
        // 显示设备对话框
        showDevicesDialog();
    }

    /**
     * 显示设备对话框
     */
    private void showDevicesDialog() {
        new SelectDeviceDialog(this, devices, deviceInfo -> {
            connectRemotePa(deviceInfo.getDeviceId(), GameRemoteProxy.REQUEST_START_ABILITY);
        }).show();
    }

    /**
     * 初始化本地端视图
     * @param intent
     */
    private void initLocalView(Intent intent) {
        LogUtil.info(TAG, "initGame");
        // 获取主界面传过来的参数, 如果为空, 使用默认值3列阵
        int count = intent.getIntParam(CommonData.KEY_COUNT, 3);
        // 本地设备Id
        localDeviceId =
                KvManagerFactory.getInstance().createKvManager(new KvManagerConfig(this)).getLocalDeviceInfo().getId();
        // 是否为本地端
        isLocal = intent.getBooleanParam(CommonData.KEY_IS_LOCAL, true);

        initGame(count);
    }

    /**
     * 初始化远程端视图
     * @param intent
     */
    private void initRemoteView(Intent intent) {
        if (!isLocal) {
            remoteDeviceId = intent.getStringParam(CommonData.KEY_REMOTE_DEVICEID);
            connectRemotePa(remoteDeviceId, GameRemoteProxy.REQUEST_SEND_DATA);
            updateDataInfo(intent);
            // 在UI主线程更新
            getUITaskDispatcher().delayDispatch(this::setGameUI, DELAY_TIME);
        }
    }

    /**
     * 连接远程Service PA
     * @param deviceId
     * @param requestType
     */
    private void connectRemotePa(String deviceId, int requestType) {
        if (!deviceId.isEmpty()) {
            Intent connectPaIntent = new Intent();
            Operation operation = new Intent.OperationBuilder().withDeviceId(deviceId)
                    .withBundleName(getBundleName())
                    .withAbilityName(CommonData.GAME_SERVICE_NAME)
                    .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
                    .build();
            connectPaIntent.setOperation(operation);

            conn = new IAbilityConnection() {
                @Override
                public void onAbilityConnectDone(ElementName elementName, IRemoteObject remote, int resultCode) {
                    LogUtil.info(TAG, "已连接上, 开始远程代理发送数据 1......");
                    // 已连接上, 开始远程代理发送数据
                    connectAbilityRemoteProxy(elementName, remote, requestType);
                }

                @Override
                public void onAbilityDisconnectDone(ElementName elementName, int resultCode) {
                    disconnectAbility(this);
                    LogUtil.info(TAG, "onAbilityDisconnectDone......");
                }
            };
            // 开始连接Ability Service PA
            isConnected = getContext().connectAbility(connectPaIntent, conn);
        }
    }

    /**
     * 已连接上, 开始远程代理发送数据
     * @param elementName
     * @param remote
     * @param requestType
     */
    private void connectAbilityRemoteProxy(ElementName elementName, IRemoteObject remote, int requestType) {
        proxy = new GameRemoteProxy(remote);
        LogUtil.info(TAG, "已连接上, 开始远程代理发送数据 2");
        if (proxy != null) {
            try {
                // 发送数据
                proxy.senDataToRemote(requestType);
            } catch (RemoteException e) {
                LogUtil.error(TAG, "onAbilityConnectDone RemoteException");
            }
        }
    }

    /**
     * 初始化游戏界面
     * @param count
     */
    private void initGame(int count) {
        LogUtil.info(TAG, "initGame");
        currentCount = count;
        arrayNumber = -1;
        beforeButton = null;
        flag = 1;
        startTime = new Date().getTime();

        TableLayout table = (TableLayout)findComponentById(ResourceTable.Id_table);
        table.setColumnCount(count);
        table.setRowCount(count);
        table.removeAllComponents();

        int deviceWidth = getContext().getResourceManager().getDeviceCapability().width;
        int btnHeight = (deviceWidth - (count-1) * 20) / count;

        TableLayout.LayoutConfig tlc = new TableLayout.LayoutConfig(0, vp2px(btnHeight));
        tlc.setMargins(20,20,20,20);
        tlc.columnSpec = TableLayout.specification(TableLayout.DEFAULT, 1, 1.0f);
        tlc.rowSpec = TableLayout.specification(TableLayout.DEFAULT, 1);

        arrayNumber = count * count;
        String[] btnTextArr = genRandomNums(arrayNumber);
        for (int i=0; i<arrayNumber; i++) {
            Button btn = new Button(getContext());
            btn.setId(i);
//            btn.setText((i+1)+"");
            btn.setText( btnTextArr[i] );
            btn.setTextColor(new Color(Color.rgb(3, 131, 144)));
            ShapeElement element = new ShapeElement();
            element.setRgbColor(new RgbColor(0x20e0efFF));
            element.setShape(ShapeElement.OVAL);
            element.setStroke(5, new RgbColor(0x70c9cdFF));
            btn.setBackground(element);
            btn.setTextSize(240/count);
            btn.setFont(Font.DEFAULT_BOLD);
            btn.setLayoutConfig(tlc);

            table.addComponent(btn);
        }

        // 设置表格布局内所有按钮事件
        setBtnListener(ResourceTable.Id_table);
    }

    /**
     * 随机生成1-count参数的整数
     * @param count
     * @return
     */
    private String[] genRandomNums(int count) {
        Random random = new Random(System.currentTimeMillis());
        String[] array = new String[count];
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < count; i++) {
            list.add(String.valueOf(i+1));//集合里存 0-count
        }
        for (int j = 0; j < count; j++) {
            int s = random.nextInt(list.size());//从集合长度,随机产生一个int
            array[j] = list.get(s); //获取集合下标的数
            //集合中每次移除添加得数,集合的长度也减一
            list.remove(s);

        }
        return array;
    }

    /**
     * 监听表格内所有按钮的事件
     * @param tableLayout
     */
    private void setBtnListener(int tableLayout) {
        TableLayout table = (TableLayout)findComponentById(tableLayout);
        int childNum = table.getChildCount();
        for (int index = 0; index < childNum; index++) {
            Button child = (Button)(table.getComponentAt(index));
            child.setClickedListener(new Component.ClickedListener() {
                @Override
                public void onClick(Component component) {
                    if (component instanceof Button) {
                        Button button = (Button)component;
                        String currentNum = button.getText();
                        setButtonNormal(beforeButton);

                        // 判断是否点击最后一个数字,如果是,弹出所用时间
                        if (flag == arrayNumber) {
                            long endTime = new Date().getTime();
                            double time = (endTime - startTime) * 1.0 / 1000;
                            Button btnTrophy = (Button)findComponentById(ResourceTable.Id_btnTrophy);
                            String timeStr = time + "\"";
                            btnTrophy.setText(timeStr);
                            popupCertificate(timeStr);
                        }

                        // 如果点击的数字和当前的一样,修改背景色
                        if (flag == Integer.valueOf(currentNum)) {
                            ShapeElement element = new ShapeElement();
                            element.setRgbColor(new RgbColor(0xf8c129FF));
                            element.setShape(ShapeElement.OVAL);
                            element.setStroke(5, new RgbColor(0xffdf18FF));
                            button.setBackground(element);

                            button.setTextColor(new Color(Color.rgb(255, 237, 125)));
                            // 生成下一个数字
                            flag++;
                            // 记录当前按钮,方便点击下一次时,恢复默认状态颜色
                            beforeButton = button;
                        }

                        // 发送数据更新远程设备
                        senDataToRemoteFun();
                    }
                }
            });
        }
    }

    /**
     * 恢复按钮为默认状态颜色
     * @param button
     */
    private void setButtonNormal(Button button) {
        if (button == null) return;
        ShapeElement element = new ShapeElement();
        element.setRgbColor(new RgbColor(0x20e0efFF));
        element.setShape(ShapeElement.OVAL);
        element.setStroke(5, new RgbColor(0x70c9cdFF));
        button.setBackground(element);
        button.setTextColor(new Color(Color.rgb(7, 135, 148)));
    }

    /**
     * 页脚初始化
     */
    private void footerBar() {
        // 获取帮助
        Button btnHelp = (Button)findComponentById(ResourceTable.Id_btnHelp);
        btnHelp.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                CommonDialog helpDialog = new CommonDialog(getContext());
                helpDialog.setTitleText("  帮助");
                helpDialog.setTitleSubText(" ");
                helpDialog.setContentText("    以最快速度从1选到"+arrayNumber);
                helpDialog.setButton(IDialog.BUTTON1, "确定", (iDialog, i) -> iDialog.destroy());
                helpDialog.setSize(800, MATCH_CONTENT);
                helpDialog.setAlignment(LayoutAlignment.HORIZONTAL_CENTER);
                helpDialog.show();
            }
        });

    }

    /**
     * 挑战结果对话框
     * @param time
     */
    private void popupCertificate(String time) {
        // 初始一个公共对话框
        dialog = new CommonDialog(getContext());
        // 对话框内容为一张图片
        dialog.setContentImage(ResourceTable.Media_certificate);
        // 背景为透明
        dialog.setTransparent(true);
        // 查找对话框容器组件
        Component container = LayoutScatter.getInstance(getContext()).parse(ResourceTable.Layout_dialog_certificate, null, false);
        // 从对话框容器组件查找显示时间文本组件
        Text txtTime = (Text)container.findComponentById(ResourceTable.Id_txtTime);
        // 显示最终使用时间,单位为秒
        txtTime.setText(time);

        // 返回游戏首页
        Image btnGoHome = (Image)container.findComponentById(ResourceTable.Id_btnGoHome);
        btnGoHome.setClickedListener(this::onClickGoHome);
        // 再来一次
        Image btnTryAgain = (Image)container.findComponentById(ResourceTable.Id_btnTryAgain);
        btnTryAgain.setClickedListener(this::onClickTryAgain);

        // 设置对话框内容为自定义组件
        dialog.setContentCustomComponent(container);
        // 设置对话框宽度和高度为内容宽高
        dialog.setSize(MATCH_CONTENT, MATCH_CONTENT);
        // 显示对话框
        dialog.show();
    }

    /**
     * 返回游戏首页
     * @param component
     */
    private void onClickGoHome(Component component) {
        // 关闭对话框
        dialog.destroy();
        // 终结当前Albility
        terminateAbility();
    }

    /**
     * 再来一次
     * @param component
     */
    private void onClickTryAgain(Component component) {
        // 关闭对话框
        dialog.destroy();
        // 初始化游戏界面
        initGame(currentCount);
    }

    /**
     * vp转px
     * @param vp
     * @return
     */
    private int vp2px(float vp) {
        return AttrHelper.vp2px(vp, getContext());
    }

    @Override
    public void onActive() {
        super.onActive();
    }

    @Override
    public void onForeground(Intent intent) {
        super.onForeground(intent);
    }

    /**
     * 游戏远程代理
     */
    class GameRemoteProxy implements IRemoteBroker {
        // 结果码
        private static final int ERR_OK = 0;
        // 请求开始Ability码
        private static final int REQUEST_START_ABILITY = 1;
        // 请求发达数据码
        private static final int REQUEST_SEND_DATA = 2;
        // 远程对象
        private final IRemoteObject remote;
        // 构造方法
        GameRemoteProxy(IRemoteObject remote) {
            this.remote = remote;
        }

        @Override
        public IRemoteObject asObject() {
            return remote;
        }

        /**
         * 发送数据到远程端
         * @param requestType
         * @throws RemoteException
         */
        private void senDataToRemote(int requestType) throws RemoteException {
            MessageParcel data = MessageParcel.obtain();
            MessageParcel reply = MessageParcel.obtain();
            try {
                isLocal = false;
                // 本地端设备Id
                data.writeString(localDeviceId);
                // 是否为本地端
                data.writeBoolean(isLocal);
                // 记录当前要点击的数字
                data.writeInt(flag);
                // 记录开始时间
                data.writeLong(startTime);
                // 当前排列数
                data.writeInt(arrayNumber);
                // 当前列阵数
                data.writeInt(currentCount);

                MessageOption option = new MessageOption(MessageOption.TF_SYNC);
                remote.sendRequest(requestType, data, reply, option);
                int ec = reply.readInt();
                if (ec != ERR_OK) {
                    LogUtil.error(TAG, "ec != ERR_OK RemoteException");
                }
            } catch (RemoteException e) {
                LogUtil.error(TAG, "RemoteException");
            } finally {
                data.reclaim();
                reply.reclaim();
            }
        }
    }

    /**
     * 订阅事件
     */
    private void subscribe() {
        MatchingSkills matchingSkills = new MatchingSkills();
        matchingSkills.addEvent(CommonData.GAME_EVENT);
        matchingSkills.addEvent(CommonEventSupport.COMMON_EVENT_SCREEN_ON);
        CommonEventSubscribeInfo subscribeInfo = new CommonEventSubscribeInfo(matchingSkills);
        subscriber = new MyCommonEventSubscriber(subscribeInfo);
        try {
            LogUtil.info("", "PICTURE_GAME_EVENT subscribeCommonEvent");
            CommonEventManager.subscribeCommonEvent(subscriber);
        } catch (RemoteException e) {
            LogUtil.error("", "subscribeCommonEvent occur exception.");
        }
    }

    /**
     * 定义内部公共事件
     */
    class MyCommonEventSubscriber extends CommonEventSubscriber {
        MyCommonEventSubscriber(CommonEventSubscribeInfo info) {
            super(info);
        }

        @Override
        public void onReceiveEvent(CommonEventData commonEventData) {
            reciveTime = System.currentTimeMillis();
            LogUtil.info(TAG, "onReceiveEvent, sendTime - reciveTime is " + (sendTime - reciveTime));
            if (Math.abs(sendTime - reciveTime) <= MAX_RECIVE_TIME) {
                LogUtil.info(TAG, "almost at the same time, do not handle recive msg");
                isShare = false;
                new ToastDialog(getContext()).setText("操作冲突，后续独立").setAlignment(LayoutAlignment.CENTER).show();
                return;
            }

            Intent intent = commonEventData.getIntent();
            updateDataInfo(intent);
        }
    }

    /**
     * 点击每个数字按钮后,调用更新所有变量
     */
    private void senDataToRemoteFun() {
        if (!isShare) {
            LogUtil.info(TAG, "should not senDataToRemote");
            return;
        }
        sendTime = System.currentTimeMillis();
        LogUtil.info(TAG, "senDataToRemote, and sendTime is " + sendTime);
        if (isConnected && proxy != null) {
            try {
                proxy.senDataToRemote(GameRemoteProxy.REQUEST_SEND_DATA);
            } catch (RemoteException e) {
                LogUtil.info(TAG, "processEvent RemoteException");
            }
        } else {
            LogUtil.info(TAG, "processEvent RemoteException Error");
        }
    }

    /**
     * 更新所有变量信息
     * @param intent
     */
    private void updateDataInfo(Intent intent) {
        remoteDeviceId = intent.getStringParam(CommonData.KEY_REMOTE_DEVICEID);
        isLocal = intent.getBooleanParam(CommonData.KEY_IS_LOCAL, true);
        flag = intent.getIntParam(CommonData.KEY_FLAG, -1);
        startTime = intent.getLongParam(CommonData.KEY_START_TIME, -1);
        arrayNumber = intent.getIntParam(CommonData.KEY_ARRAY_NUMBER, -1);
        currentCount = intent.getIntParam(CommonData.KEY_CURRENT_COUNT, -1);
        LogUtil.info(TAG, "******** 是否本地端: " + isLocal);
        System.out.println("******** 是否本地端: " + isLocal);
    }

    /**
     * 更新流转后标题变化
     */
    private void setGameUI() {
        headerBar();
        // 设置表格布局内所有按钮事件
//        setBtnListener(ResourceTable.Id_table);
    }
}
